package com.up.swagger.config;

import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.mappers.*;

import java.util.Iterator;
import java.util.List;

/**
 * @author: baiMl
 * @title: SwaggerAutoConfiguration
 * @projectName: 海联技术中台V2.0
 * @depart 软件事业部
 * @description:
 * @date: 2022/5/19 9:54
 */
@Configuration
@ConditionalOnProperty(prefix = "spring.swagger",value = "enable",havingValue = "true")
@EnableConfigurationProperties(SwaggerProperties.class)
@EnableSwagger2
@EnableSwaggerBootstrapUI
@ConditionalOnWebApplication
public class SwaggerAutoConfiguration {

    private final SwaggerProperties swaggerProperties;

    /**
     * 定义分隔符
     */
    private  final String SPLITTER = ";";

    public SwaggerAutoConfiguration(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }

    @Bean("ServiceModelToSwagger2Mapper")
    @Primary
    @ConditionalOnWebApplication
    public ServiceModelToSwagger2MapperImpl swagger2Mapper(ModelMapper modelMapper,
                                                           ParameterMapper parameterMapper,
                                                           SecurityMapper securityMapper,
                                                           LicenseMapper licenseMapper,
                                                           VendorExtensionsMapper vendorExtensionsMapper,
                                                           MessageSource messageSource) {
        return new ServiceModelToSwagger2MapperImpl(modelMapper, parameterMapper,
                securityMapper, licenseMapper, vendorExtensionsMapper, messageSource);
    }

    @Bean
    public Docket docketApi() {
        Docket docket = null;
        List<String> allSwaggerPackage = swaggerProperties.getPackages();
        if (allSwaggerPackage == null || allSwaggerPackage.isEmpty()) {
            throw new RuntimeException("swagger package is must set ! now is null");
        }
        boolean isSingleSwaggerPackage=allSwaggerPackage.size() == 1;

        if (isSingleSwaggerPackage) {
            Iterator<String> iterator = allSwaggerPackage.iterator();
            if (iterator.hasNext()) {
                String basePackage = iterator.next();
                docket = new Docket(DocumentationType.SWAGGER_2)
                        .apiInfo(apiInfo())
                        .select()
                        // 自行修改为自己的包路径
                        .apis(RequestHandlerSelectors.basePackage(basePackage))
                        .paths(PathSelectors.any()).build();
            }
        } else {

            String basePackage = allSwaggerPackage.stream().reduce((a, b) -> a.concat(SPLITTER).concat(b)).get();
            docket = new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    // 自行修改为自己的包路径
                    .apis(basePackage(basePackage))
                    .paths(PathSelectors.any()).build();
        }
        return docket;
    }

    private Predicate<RequestHandler> basePackage(final String basePackage) {
        return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
    }

    private Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackage.split(SPLITTER)) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    private Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }


    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(swaggerProperties.getTitle())
                .description(swaggerProperties.getDescription())
                .version(swaggerProperties.getVersion())
                .build();
    }


}
